#include "localcapture.h"
#include <boost/thread/thread.hpp>
#include "trace.h"
#include "libpfcmu/linux_aio.h"

LocalCapture::LocalCapture(const char *host, unsigned int fps, unsigned int camera, double shutter, double gain) :
        HOSTNAME(host), m_Fps(fps), FRAMEINC(fps==25 ? 4 : 1), m_CameraArrayIdx(camera),
        m_Shutter(shutter), m_Gain(gain),m_StartTimeCode(0),m_Frames(0)
{
    isRunning = false;
    isCapturing = false;
    isInit = true;
}

void LocalCapture::run(unsigned int c_ringnum)
{
    //new thread
    m_CRingNum = c_ringnum;
    boost::thread captureTrd(boost::bind(&LocalCapture::localRun, this, m_Fps, m_CRingNum, m_CameraArrayIdx));
}

timestamp_t LocalCapture::startCapture(timestamp_t start, unsigned int n, std::string fname, unsigned int d_ringnum)
{
    if(isRunning)
    {
        if(!isInit)
        {
            if(!isCapturing)
            {
                const unsigned int FRAMEDELAY = 400;
                const unsigned int FRAMECOUNTUNIT = 1000;
                m_DRingNum = d_ringnum;
                m_Frames = n;
                m_StartTimeCode = (start+FRAMEDELAY)/FRAMECOUNTUNIT*FRAMECOUNTUNIT+FRAMECOUNTUNIT;
                m_OutFName = fname;
                TRACE(1, "%s: mFrames is %d and input is %d\n",HOSTNAME, m_Frames, n);
                TRACE(1, "%s: Capture should start on %llu. Save %d frames to %s.\n", HOSTNAME, m_StartTimeCode, m_Frames, m_OutFName.c_str());
                isCapturing = true;
                return m_StartTimeCode;
            }
        }
    }
    return 0;
}

timestamp_t LocalCapture::startCapture(unsigned int n, std::string fname, unsigned int d_ringnum)
{
    return this->startCapture(ts_curr,n,fname,d_ringnum);
}

void LocalCapture::localRun(unsigned int fps, unsigned int c_ringnum, unsigned int camera)
{
    PFCMU::Capture capture;
    int localFps;
    /*Check fps parameter. 25@640x480 or 100@320*240*/
    if(fps != 25 && fps != 100)
    {
        localFps = 25;
    }
    else
    {
        localFps = fps;
    }

    /*Multi thread. Check first*/
    if(!isRunning)
    {
        isRunning = true;
        TRACE(1, "%s: Init ProFUSION\n", HOSTNAME);
        capture.init(camera, localFps);
        PFCMU::libaio::writer_t *writer = NULL;
        TRACE(1,"%s: ProFUSION start\n", HOSTNAME);
        capture.start(c_ringnum);

        capture.set_shutter(m_Shutter);
        capture.set_gain(m_Gain);
        TRACE(1,"%s: Shutter set to %f, Gain set to %f.\n", HOSTNAME, m_Shutter, m_Gain);
        TRACE(1, "%s: ProFUSION is running\n", HOSTNAME);

        ts_prev = 0;

        //kill old frames
        for(unsigned int i=0;i<c_ringnum+1;i++)
        {
            currImg = capture.grab();
            const timestamp_t ts = capture.get_framecount();
            ts_prev = ts;
        }

        int m_CaptureCount = 0;

        TRACE(1,"%s: Start main loop\n", HOSTNAME);
        isInit = false;
        while(isRunning)
        {
            currImg = capture.grab();
            ts_curr = capture.get_framecount();
            capture.embed_framecount();

            /*Capture part*/
            if(isCapturing)
            {
                if(writer->is_initialized())
                {
                    //test start frame time code
                    if(ts_curr < m_StartTimeCode)
                    {
                        m_CaptureCount = 0;
                        m_FrameErrorCount = 0;
                    }
                    else if(m_CaptureCount < m_Frames)
                    {
                        int id = writer->get_available_slot_id();
                        capture.copy_all(writer->buf(id));
                        writer->write(id, m_CaptureCount);
                        m_CaptureCount++;
                        if(ts_curr-ts_prev>4)
                        {
                            m_FrameErrorCount++;
                            TRACE(1, "%s: Frame dropped between %llu and %llu.\n", HOSTNAME, ts_prev, ts_curr);
                        }
                        //TRACE(1, "%s: Capturing frame %llu, %d of %d frames.\n", HOSTNAME, ts_curr, m_CaptureCount+1, m_Frames);
                        m_CaptureCount++;
                        TRACE(1, "%s: Capturing frame %llu, %d of %d frames.\n", HOSTNAME, ts_curr, m_CaptureCount+1, m_Frames);
                    }
                    else if(m_CaptureCount >= m_Frames)
                    {
                        TRACE(1, "%s: Capture finished on frame %llu\n", HOSTNAME, ts_curr);
                        writer.clean();
                        isCapturing = false;
                    }
                }
                else
                {
                    //init writer
                    TRACE(1, "%s: Init hard drive writer for %s.\n", HOSTNAME, m_OutFName.c_str());
                    writer.init(m_OutFName.c_str(),capture.memsize(),m_DRingNum,m_Frames,4096);
                    m_CaptureCount = 0;
                }
            }

            prevImg = currImg;
            ts_prev = ts_curr;


        }
        capture.stop();
        isRunning = false;
    }
}

PF_EZImage *LocalCapture::getLiveImg()
{
    return prevImg;
}

void LocalCapture::setShutter(double shutter)
{
    //TODO
}

void LocalCapture::setGain(double gain)
{
    //TODO
}

bool LocalCapture::checkRunning() const
{
    return (isRunning&& (!isInit));
}

unsigned int LocalCapture::getFps() const
{
    return m_Fps;
}
